Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.64% covered (success)
96.64%
1207 / 1249
56.52% covered (warning)
56.52%
13 / 23
CRAP
0.00% covered (danger)
0.00%
0 / 1
PptCharts
96.64% covered (success)
96.64%
1207 / 1249
56.52% covered (warning)
56.52%
13 / 23
219
0.00% covered (danger)
0.00%
0 / 1
 render
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
5.02
 writeChart
84.72% covered (warning)
84.72%
61 / 72
0.00% covered (danger)
0.00%
0 / 1
8.23
 writeSpreadsheet
93.75% covered (success)
93.75%
30 / 32
0.00% covered (danger)
0.00%
0 / 1
6.01
 writeElementWithValAttribute
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 writeSingleValueOrReference
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
2
 writeMultipleValuesOrReference
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
7
 writeTitle
100.00% covered (success)
100.00%
47 / 47
100.00% covered (success)
100.00%
1 / 1
4
 writePlotArea
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
12
 writeLegend
100.00% covered (success)
100.00%
47 / 47
100.00% covered (success)
100.00%
1 / 1
5
 writeLayout
76.92% covered (warning)
76.92%
20 / 26
0.00% covered (danger)
0.00%
0 / 1
5.31
 writeTypeArea
93.33% covered (success)
93.33%
42 / 45
0.00% covered (danger)
0.00%
0 / 1
9.02
 writeTypeBar
93.20% covered (success)
93.20%
96 / 103
0.00% covered (danger)
0.00%
0 / 1
17.09
 writeTypeBar3D
96.81% covered (success)
96.81%
91 / 94
0.00% covered (danger)
0.00%
0 / 1
15
 writeTypeDoughnut
95.40% covered (success)
95.40%
83 / 87
0.00% covered (danger)
0.00%
0 / 1
20
 writeTypePie
95.06% covered (success)
95.06%
77 / 81
0.00% covered (danger)
0.00%
0 / 1
15
 writeTypePie3D
100.00% covered (success)
100.00%
78 / 78
100.00% covered (success)
100.00%
1 / 1
13
 writeTypeLine
100.00% covered (success)
100.00%
83 / 83
100.00% covered (success)
100.00%
1 / 1
13
 writeTypeRadar
100.00% covered (success)
100.00%
81 / 81
100.00% covered (success)
100.00%
1 / 1
13
 writeTypeScatter
100.00% covered (success)
100.00%
87 / 87
100.00% covered (success)
100.00%
1 / 1
16
 writeChartRelationships
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 writeSeriesMarker
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
4
 writeAxis
99.42% covered (success)
99.42%
171 / 172
0.00% covered (danger)
0.00%
0 / 1
26
 writeAxisGridlines
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * This file is part of PHPPresentation - A pure PHP library for reading and writing
4 * presentations documents.
5 *
6 * PHPPresentation is free software distributed under the terms of the GNU Lesser
7 * General Public License version 3 as published by the Free Software Foundation.
8 *
9 * For the full copyright and license information, please read the LICENSE
10 * file that was distributed with this source code. For the full list of
11 * contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
12 *
13 * @see        https://github.com/PHPOffice/PHPPresentation
14 *
15 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16 */
17
18declare(strict_types=1);
19
20namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007;
21
22use PhpOffice\Common\Adapter\Zip\ZipInterface;
23use PhpOffice\Common\Drawing as CommonDrawing;
24use PhpOffice\Common\XMLWriter;
25use PhpOffice\PhpPresentation\Exception\FileRemoveException;
26use PhpOffice\PhpPresentation\Exception\UndefinedChartTypeException;
27use PhpOffice\PhpPresentation\PhpPresentation;
28use PhpOffice\PhpPresentation\Shape\Chart;
29use PhpOffice\PhpPresentation\Shape\Chart\Gridlines;
30use PhpOffice\PhpPresentation\Shape\Chart\Legend;
31use PhpOffice\PhpPresentation\Shape\Chart\PlotArea;
32use PhpOffice\PhpPresentation\Shape\Chart\Title;
33use PhpOffice\PhpPresentation\Shape\Chart\Type\Area;
34use PhpOffice\PhpPresentation\Shape\Chart\Type\Bar;
35use PhpOffice\PhpPresentation\Shape\Chart\Type\Bar3D;
36use PhpOffice\PhpPresentation\Shape\Chart\Type\Doughnut;
37use PhpOffice\PhpPresentation\Shape\Chart\Type\Line;
38use PhpOffice\PhpPresentation\Shape\Chart\Type\Pie;
39use PhpOffice\PhpPresentation\Shape\Chart\Type\Pie3D;
40use PhpOffice\PhpPresentation\Shape\Chart\Type\Radar;
41use PhpOffice\PhpPresentation\Shape\Chart\Type\Scatter;
42use PhpOffice\PhpPresentation\Style\Border;
43use PhpOffice\PhpPresentation\Style\Fill;
44use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
45use PhpOffice\PhpSpreadsheet\IOFactory;
46use PhpOffice\PhpSpreadsheet\Spreadsheet;
47
48class PptCharts extends AbstractDecoratorWriter
49{
50    public function render(): ZipInterface
51    {
52        for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
53            $shape = $this->getDrawingHashTable()->getByIndex($i);
54            if ($shape instanceof Chart) {
55                $this->getZip()->addFromString('ppt/charts/' . $shape->getIndexedFilename(), $this->writeChart($shape));
56
57                if ($shape->hasIncludedSpreadsheet()) {
58                    $this->getZip()->addFromString('ppt/charts/_rels/' . $shape->getIndexedFilename() . '.rels', $this->writeChartRelationships($shape));
59                    $pFilename = tempnam(sys_get_temp_dir(), 'PhpSpreadsheet');
60                    $this->getZip()->addFromString('ppt/embeddings/' . $shape->getIndexedFilename() . '.xlsx', $this->writeSpreadsheet($this->getPresentation(), $shape, $pFilename . '.xlsx'));
61
62                    // remove temp file
63                    if (false === @unlink($pFilename)) {
64                        throw new FileRemoveException($pFilename);
65                    }
66                }
67            }
68        }
69
70        return $this->getZip();
71    }
72
73    /**
74     * Write chart to XML format.
75     *
76     * @return string XML Output
77     */
78    protected function writeChart(Chart $chart): string
79    {
80        // Create XML writer
81        $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
82
83        // XML header
84        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
85
86        // c:chartSpace
87        $objWriter->startElement('c:chartSpace');
88        $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
89        $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
90        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
91
92        // c:date1904
93        $objWriter->startElement('c:date1904');
94        $objWriter->writeAttribute('val', '1');
95        $objWriter->endElement();
96
97        // c:lang
98        $objWriter->startElement('c:lang');
99        $objWriter->writeAttribute('val', 'en-US');
100        $objWriter->endElement();
101
102        // c:chart
103        $objWriter->startElement('c:chart');
104
105        // Title?
106        if ($chart->getTitle()->isVisible()) {
107            // Write title
108            $this->writeTitle($objWriter, $chart->getTitle());
109        }
110
111        // c:autoTitleDeleted
112        $objWriter->startElement('c:autoTitleDeleted');
113        $objWriter->writeAttribute('val', $chart->getTitle()->isVisible() ? '0' : '1');
114        $objWriter->endElement();
115
116        // c:view3D
117        $objWriter->startElement('c:view3D');
118
119        // c:rotX
120        $objWriter->startElement('c:rotX');
121        $objWriter->writeAttribute('val', $chart->getView3D()->getRotationX());
122        $objWriter->endElement();
123
124        // c:hPercent
125        $hPercent = $chart->getView3D()->getHeightPercent();
126        $objWriter->writeElementIf(null != $hPercent, 'c:hPercent', 'val', $hPercent);
127
128        // c:rotY
129        $objWriter->startElement('c:rotY');
130        $objWriter->writeAttribute('val', $chart->getView3D()->getRotationY());
131        $objWriter->endElement();
132
133        // c:depthPercent
134        $objWriter->startElement('c:depthPercent');
135        $objWriter->writeAttribute('val', $chart->getView3D()->getDepthPercent());
136        $objWriter->endElement();
137
138        // c:rAngAx
139        $objWriter->startElement('c:rAngAx');
140        $objWriter->writeAttribute('val', $chart->getView3D()->hasRightAngleAxes() ? '1' : '0');
141        $objWriter->endElement();
142
143        // c:perspective
144        $objWriter->startElement('c:perspective');
145        $objWriter->writeAttribute('val', $chart->getView3D()->getPerspective());
146        $objWriter->endElement();
147
148        $objWriter->endElement();
149
150        // Write plot area
151        $this->writePlotArea($objWriter, $chart->getPlotArea(), $chart);
152
153        // Legend?
154        if ($chart->getLegend()->isVisible()) {
155            // Write legend
156            $this->writeLegend($objWriter, $chart->getLegend());
157        }
158
159        // c:plotVisOnly
160        $objWriter->startElement('c:plotVisOnly');
161        $objWriter->writeAttribute('val', '1');
162        $objWriter->endElement();
163
164        // c:dispBlanksAs
165        $objWriter->startElement('c:dispBlanksAs');
166        $objWriter->writeAttribute('val', $chart->getDisplayBlankAs());
167        $objWriter->endElement();
168
169        $objWriter->endElement();
170
171        // c:spPr
172        $objWriter->startElement('c:spPr');
173
174        // Fill
175        $this->writeFill($objWriter, $chart->getFill());
176
177        // Border
178        if (Border::LINE_NONE != $chart->getBorder()->getLineStyle()) {
179            $this->writeBorder($objWriter, $chart->getBorder(), '');
180        }
181
182        // Shadow
183        if ($chart->getShadow()->isVisible()) {
184            // a:effectLst
185            $objWriter->startElement('a:effectLst');
186
187            // a:outerShdw
188            $objWriter->startElement('a:outerShdw');
189            $objWriter->writeAttribute('blurRad', CommonDrawing::pixelsToEmu($chart->getShadow()->getBlurRadius()));
190            $objWriter->writeAttribute('dist', CommonDrawing::pixelsToEmu($chart->getShadow()->getDistance()));
191            $objWriter->writeAttribute('dir', CommonDrawing::degreesToAngle((int) $chart->getShadow()->getDirection()));
192            $objWriter->writeAttribute('algn', $chart->getShadow()->getAlignment());
193            $objWriter->writeAttribute('rotWithShape', '0');
194
195            $this->writeColor($objWriter, $chart->getShadow()->getColor(), $chart->getShadow()->getAlpha());
196
197            $objWriter->endElement();
198
199            $objWriter->endElement();
200        }
201
202        $objWriter->endElement();
203
204        // External data?
205        if ($chart->hasIncludedSpreadsheet()) {
206            // c:externalData
207            $objWriter->startElement('c:externalData');
208            $objWriter->writeAttribute('r:id', 'rId1');
209
210            // c:autoUpdate
211            $objWriter->startElement('c:autoUpdate');
212            $objWriter->writeAttribute('val', '0');
213            $objWriter->endElement();
214
215            $objWriter->endElement();
216        }
217
218        $objWriter->endElement();
219
220        // Return
221        return $objWriter->getData();
222    }
223
224    /**
225     * Write chart to XML format.
226     *
227     * @return string String output
228     */
229    protected function writeSpreadsheet(PhpPresentation $presentation, Chart $chart, string $tempName): string
230    {
231        // Create new spreadsheet
232        $spreadsheet = new Spreadsheet();
233
234        // Set properties
235        $title = $chart->getTitle()->getText();
236        if (0 == strlen($title)) {
237            $title = 'Chart';
238        }
239        $spreadsheet->getProperties()
240            ->setCreator(
241                $presentation->getDocumentProperties()->getCreator()
242            )
243            ->setLastModifiedBy(
244                $presentation->getDocumentProperties()->getLastModifiedBy()
245            )
246            ->setTitle($title);
247
248        // Add chart data
249        $sheet = $spreadsheet->setActiveSheetIndex(0);
250        $sheet->setTitle('Sheet1');
251
252        // Write series
253        $seriesIndex = 0;
254        foreach ($chart->getPlotArea()->getType()->getSeries() as $series) {
255            // Title
256            $sheet->setCellValueByColumnAndRow(2 + $seriesIndex, 1, $series->getTitle());
257
258            // X-axis
259            $axisXData = array_keys($series->getValues());
260            $numAxisXData = count($axisXData);
261            for ($i = 0; $i < $numAxisXData; ++$i) {
262                $sheet->setCellValueByColumnAndRow(1, $i + 2, $axisXData[$i]);
263            }
264
265            // Y-axis
266            $axisYData = array_values($series->getValues());
267            $numAxisYData = count($axisYData);
268            for ($i = 0; $i < $numAxisYData; ++$i) {
269                $sheet->setCellValueByColumnAndRow(2 + $seriesIndex, $i + 2, $axisYData[$i]);
270            }
271
272            ++$seriesIndex;
273        }
274
275        // Save to string
276        $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
277        $writer->save($tempName);
278
279        // Load file in memory
280        $returnValue = file_get_contents($tempName);
281        if (false === @unlink($tempName)) {
282            throw new FileRemoveException($tempName);
283        }
284
285        return $returnValue;
286    }
287
288    /**
289     * Write element with value attribute.
290     *
291     * @param XMLWriter $objWriter XML Writer
292     */
293    protected function writeElementWithValAttribute(XMLWriter $objWriter, string $elementName, string $value): void
294    {
295        $objWriter->startElement($elementName);
296        $objWriter->writeAttribute('val', $value);
297        $objWriter->endElement();
298    }
299
300    /**
301     * Write single value or reference.
302     *
303     * @param XMLWriter $objWriter XML Writer
304     */
305    protected function writeSingleValueOrReference(XMLWriter $objWriter, bool $isReference, string $value, string $reference): void
306    {
307        if (!$isReference) {
308            // Value
309            $objWriter->writeElement('c:v', $value);
310
311            return;
312        }
313
314        // Reference and cache
315        // c:strRef
316        $objWriter->startElement('c:strRef');
317        // c:strRef/c:f
318        $objWriter->writeElement('c:f', $reference);
319        // c:strRef/c:strCache
320        $objWriter->startElement('c:strCache');
321        // c:strRef/c:strCache/c:ptCount
322        $objWriter->startElement('c:ptCount');
323        $objWriter->writeAttribute('val', '1');
324        $objWriter->endElement();
325
326        // c:strRef/c:strCache/c:pt
327        $objWriter->startElement('c:pt');
328        $objWriter->writeAttribute('idx', '0');
329        // c:strRef/c:strCache/c:pt/c:v
330        $objWriter->writeElement('c:v', $value);
331        // c:strRef/c:strCache/c:pt
332        $objWriter->endElement();
333        // c:strRef/c:strCache
334        $objWriter->endElement();
335        // c:strRef
336        $objWriter->endElement();
337    }
338
339    /**
340     * Write series value or reference.
341     *
342     * @param XMLWriter $objWriter XML Writer
343     * @param array<int, mixed> $values
344     */
345    protected function writeMultipleValuesOrReference(XMLWriter $objWriter, bool $isReference, array $values, string $reference): void
346    {
347        // c:strLit / c:numLit
348        // c:strRef / c:numRef
349        $referenceType = ($isReference ? 'Ref' : 'Lit');
350
351        // Get data type from first non-null value
352        $dataType = array_reduce($values, function ($carry, $item) {
353            if (!isset($item)) {
354                return $carry;
355            }
356
357            return is_numeric($item) ? 'num' : 'str';
358        }, 'num');
359
360        $objWriter->startElement('c:' . $dataType . $referenceType);
361
362        $numValues = count($values);
363        if (!$isReference) {
364            // Value
365
366            // c:ptCount
367            $objWriter->startElement('c:ptCount');
368            $objWriter->writeAttribute('val', count($values));
369            $objWriter->endElement();
370
371            // Add points
372            for ($i = 0; $i < $numValues; ++$i) {
373                // c:pt
374                $objWriter->startElement('c:pt');
375                $objWriter->writeAttribute('idx', $i);
376                $objWriter->writeElement('c:v', (string) ($values[$i]));
377                $objWriter->endElement();
378            }
379        } else {
380            // Reference
381            $objWriter->writeElement('c:f', $reference);
382            $objWriter->startElement('c:' . $dataType . 'Cache');
383
384            // c:ptCount
385            $objWriter->startElement('c:ptCount');
386            $objWriter->writeAttribute('val', count($values));
387            $objWriter->endElement();
388
389            // Add points
390            for ($i = 0; $i < $numValues; ++$i) {
391                // c:pt
392                $objWriter->startElement('c:pt');
393                $objWriter->writeAttribute('idx', $i);
394                $objWriter->writeElement('c:v', (string) ($values[$i]));
395                $objWriter->endElement();
396            }
397
398            $objWriter->endElement();
399        }
400
401        $objWriter->endElement();
402    }
403
404    /**
405     * Write Title.
406     */
407    protected function writeTitle(XMLWriter $objWriter, Title $subject): void
408    {
409        // c:title
410        $objWriter->startElement('c:title');
411
412        // c:tx
413        $objWriter->startElement('c:tx');
414
415        // c:rich
416        $objWriter->startElement('c:rich');
417
418        // a:bodyPr
419        $objWriter->writeElement('a:bodyPr', null);
420
421        // a:lstStyle
422        $objWriter->writeElement('a:lstStyle', null);
423
424        // a:p
425        $objWriter->startElement('a:p');
426
427        // a:pPr
428        $objWriter->startElement('a:pPr');
429        $objWriter->writeAttribute('algn', $subject->getAlignment()->getHorizontal());
430        $objWriter->writeAttribute('fontAlgn', $subject->getAlignment()->getVertical());
431        $objWriter->writeAttribute('marL', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginLeft()));
432        $objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginRight()));
433        $objWriter->writeAttribute('indent', CommonDrawing::pixelsToEmu($subject->getAlignment()->getIndent()));
434        $objWriter->writeAttribute('lvl', $subject->getAlignment()->getLevel());
435
436        // a:defRPr
437        $objWriter->writeElement('a:defRPr', null);
438
439        $objWriter->endElement();
440
441        // a:r
442        $objWriter->startElement('a:r');
443
444        // a:rPr
445        $objWriter->startElement('a:rPr');
446        $objWriter->writeAttribute('lang', 'en-US');
447        $objWriter->writeAttribute('dirty', '0');
448        $objWriter->writeAttribute('b', ($subject->getFont()->isBold() ? 'true' : 'false'));
449        $objWriter->writeAttribute('i', ($subject->getFont()->isItalic() ? 'true' : 'false'));
450        $objWriter->writeAttribute('strike', ($subject->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
451        $objWriter->writeAttribute('sz', ($subject->getFont()->getSize() * 100));
452        $objWriter->writeAttribute('u', $subject->getFont()->getUnderline());
453        $objWriter->writeAttributeIf($subject->getFont()->isSuperScript(), 'baseline', '300000');
454        $objWriter->writeAttributeIf($subject->getFont()->isSubScript(), 'baseline', '-250000');
455
456        // Font - a:solidFill
457        $objWriter->startElement('a:solidFill');
458
459        $this->writeColor($objWriter, $subject->getFont()->getColor());
460
461        $objWriter->endElement();
462
463        // Font - a:latin
464        $objWriter->startElement('a:latin');
465        $objWriter->writeAttribute('typeface', $subject->getFont()->getName());
466        $objWriter->endElement();
467
468        $objWriter->endElement();
469
470        // a:t
471        $objWriter->writeElement('a:t', $subject->getText());
472
473        $objWriter->endElement();
474
475        // a:endParaRPr
476        $objWriter->startElement('a:endParaRPr');
477        $objWriter->writeAttribute('lang', 'en-US');
478        $objWriter->writeAttribute('dirty', '0');
479        $objWriter->endElement();
480
481        $objWriter->endElement();
482
483        $objWriter->endElement();
484
485        $objWriter->endElement();
486
487        // Write layout
488        $this->writeLayout($objWriter, $subject);
489
490        // c:overlay
491        $objWriter->startElement('c:overlay');
492        $objWriter->writeAttribute('val', '0');
493        $objWriter->endElement();
494
495        $objWriter->endElement();
496    }
497
498    /**
499     * Write Plot Area.
500     *
501     * @param XMLWriter $objWriter XML Writer
502     */
503    protected function writePlotArea(XMLWriter $objWriter, PlotArea $subject, Chart $chart): void
504    {
505        // c:plotArea
506        $objWriter->startElement('c:plotArea');
507
508        // Write layout
509        $this->writeLayout($objWriter, $subject);
510
511        // Write chart
512        $chartType = $subject->getType();
513        if ($chartType instanceof Area) {
514            $this->writeTypeArea($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
515        } elseif ($chartType instanceof Bar) {
516            $this->writeTypeBar($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
517        } elseif ($chartType instanceof Bar3D) {
518            $this->writeTypeBar3D($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
519        } elseif ($chartType instanceof Doughnut) {
520            $this->writeTypeDoughnut($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
521        } elseif ($chartType instanceof Pie) {
522            $this->writeTypePie($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
523        } elseif ($chartType instanceof Pie3D) {
524            $this->writeTypePie3D($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
525        } elseif ($chartType instanceof Line) {
526            $this->writeTypeLine($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
527        } elseif ($chartType instanceof Radar) {
528            $this->writeTypeRadar($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
529        } elseif ($chartType instanceof Scatter) {
530            $this->writeTypeScatter($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
531        } else {
532            throw new UndefinedChartTypeException();
533        }
534
535        // Write X axis?
536        if ($chartType->hasAxisX()) {
537            $this->writeAxis($objWriter, $subject->getAxisX(), Chart\Axis::AXIS_X, $chartType);
538        }
539
540        // Write Y axis?
541        if ($chartType->hasAxisY()) {
542            $this->writeAxis($objWriter, $subject->getAxisY(), Chart\Axis::AXIS_Y, $chartType);
543        }
544
545        $objWriter->endElement();
546    }
547
548    /**
549     * Write Legend.
550     *
551     * @param XMLWriter $objWriter XML Writer
552     * @param Chart\Legend $subject
553     */
554    protected function writeLegend(XMLWriter $objWriter, Legend $subject): void
555    {
556        // c:legend
557        $objWriter->startElement('c:legend');
558
559        // c:legendPos
560        $objWriter->startElement('c:legendPos');
561        $objWriter->writeAttribute('val', $subject->getPosition());
562        $objWriter->endElement();
563
564        // Write layout
565        $this->writeLayout($objWriter, $subject);
566
567        // c:overlay
568        $objWriter->startElement('c:overlay');
569        $objWriter->writeAttribute('val', '0');
570        $objWriter->endElement();
571
572        // c:spPr
573        $objWriter->startElement('c:spPr');
574
575        // Fill
576        $this->writeFill($objWriter, $subject->getFill());
577
578        // Border
579        if (Border::LINE_NONE != $subject->getBorder()->getLineStyle()) {
580            $this->writeBorder($objWriter, $subject->getBorder(), '');
581        }
582
583        $objWriter->endElement();
584
585        // c:txPr
586        $objWriter->startElement('c:txPr');
587
588        // a:bodyPr
589        $objWriter->writeElement('a:bodyPr', null);
590
591        // a:lstStyle
592        $objWriter->writeElement('a:lstStyle', null);
593
594        // a:p
595        $objWriter->startElement('a:p');
596
597        // a:pPr
598        $objWriter->startElement('a:pPr');
599        $objWriter->writeAttribute('algn', $subject->getAlignment()->getHorizontal());
600        $objWriter->writeAttribute('fontAlgn', $subject->getAlignment()->getVertical());
601        $objWriter->writeAttribute('marL', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginLeft()));
602        $objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginRight()));
603        $objWriter->writeAttribute('indent', CommonDrawing::pixelsToEmu($subject->getAlignment()->getIndent()));
604        $objWriter->writeAttribute('lvl', $subject->getAlignment()->getLevel());
605
606        // a:defRPr
607        $objWriter->startElement('a:defRPr');
608
609        $objWriter->writeAttribute('b', ($subject->getFont()->isBold() ? 'true' : 'false'));
610        $objWriter->writeAttribute('i', ($subject->getFont()->isItalic() ? 'true' : 'false'));
611        $objWriter->writeAttribute('strike', ($subject->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
612        $objWriter->writeAttribute('sz', ($subject->getFont()->getSize() * 100));
613        $objWriter->writeAttribute('u', $subject->getFont()->getUnderline());
614        $objWriter->writeAttributeIf($subject->getFont()->isSuperScript(), 'baseline', '300000');
615        $objWriter->writeAttributeIf($subject->getFont()->isSubScript(), 'baseline', '-250000');
616
617        // Font - a:solidFill
618        $objWriter->startElement('a:solidFill');
619
620        $this->writeColor($objWriter, $subject->getFont()->getColor());
621
622        $objWriter->endElement();
623
624        // Font - a:latin
625        $objWriter->startElement('a:latin');
626        $objWriter->writeAttribute('typeface', $subject->getFont()->getName());
627        $objWriter->endElement();
628
629        $objWriter->endElement();
630
631        $objWriter->endElement();
632
633        // a:endParaRPr
634        $objWriter->startElement('a:endParaRPr');
635        $objWriter->writeAttribute('lang', 'en-US');
636        $objWriter->writeAttribute('dirty', '0');
637        $objWriter->endElement();
638
639        $objWriter->endElement();
640
641        $objWriter->endElement();
642
643        $objWriter->endElement();
644    }
645
646    /**
647     * Write Layout.
648     *
649     * @param XMLWriter $objWriter XML Writer
650     * @param Legend|PlotArea|Title $subject
651     */
652    protected function writeLayout(XMLWriter $objWriter, $subject): void
653    {
654        // c:layout
655        $objWriter->startElement('c:layout');
656
657        // c:manualLayout
658        $objWriter->startElement('c:manualLayout');
659        // c:xMode
660        $objWriter->startElement('c:xMode');
661        $objWriter->writeAttribute('val', 'edge');
662        $objWriter->endElement();
663
664        // c:yMode
665        $objWriter->startElement('c:yMode');
666        $objWriter->writeAttribute('val', 'edge');
667        $objWriter->endElement();
668
669        if (0 != $subject->getOffsetX()) {
670            // c:x
671            $objWriter->startElement('c:x');
672            $objWriter->writeAttribute('val', $subject->getOffsetX());
673            $objWriter->endElement();
674        }
675
676        if (0 != $subject->getOffsetY()) {
677            // c:y
678            $objWriter->startElement('c:y');
679            $objWriter->writeAttribute('val', $subject->getOffsetY());
680            $objWriter->endElement();
681        }
682
683        if (0 != $subject->getWidth()) {
684            // c:w
685            $objWriter->startElement('c:w');
686            $objWriter->writeAttribute('val', $subject->getWidth());
687            $objWriter->endElement();
688        }
689
690        if (0 != $subject->getHeight()) {
691            // c:h
692            $objWriter->startElement('c:h');
693            $objWriter->writeAttribute('val', $subject->getHeight());
694            $objWriter->endElement();
695        }
696
697        $objWriter->endElement();
698        $objWriter->endElement();
699    }
700
701    /**
702     * Write Type Area.
703     *
704     * @param XMLWriter $objWriter XML Writer
705     * @param Chart\Type\Area $subject
706     */
707    protected function writeTypeArea(XMLWriter $objWriter, Area $subject, bool $includeSheet = false): void
708    {
709        // c:lineChart
710        $objWriter->startElement('c:areaChart');
711
712        // c:grouping
713        $objWriter->startElement('c:grouping');
714        $objWriter->writeAttribute('val', 'standard');
715        $objWriter->endElement();
716
717        // Write series
718        $seriesIndex = 0;
719        foreach ($subject->getSeries() as $series) {
720            // c:ser
721            $objWriter->startElement('c:ser');
722
723            // c:ser > c:idx
724            $objWriter->startElement('c:idx');
725            $objWriter->writeAttribute('val', $seriesIndex);
726            $objWriter->endElement();
727
728            // c:ser > c:order
729            $objWriter->startElement('c:order');
730            $objWriter->writeAttribute('val', $seriesIndex);
731            $objWriter->endElement();
732
733            // c:ser > c:tx
734            $objWriter->startElement('c:tx');
735            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(2 + $seriesIndex) . '$1' : '');
736            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
737            $objWriter->endElement();
738
739            // c:ser > c:dLbls
740            // @link : https://msdn.microsoft.com/en-us/library/documentformat.openxml.drawing.charts.areachartseries.aspx
741            $objWriter->startElement('c:dLbls');
742
743            // c:ser > c:dLbls > c:showVal
744            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
745
746            // c:ser > c:dLbls > c:showCatName
747            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
748
749            // c:ser > c:dLbls > c:showSerName
750            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
751
752            // c:ser > c:dLbls > c:showPercent
753            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
754
755            // c:ser > ##c:dLbls
756            $objWriter->endElement();
757
758            if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
759                // c:spPr
760                $objWriter->startElement('c:spPr');
761                // Write fill
762                $this->writeFill($objWriter, $series->getFill());
763                // ## c:spPr
764                $objWriter->endElement();
765            }
766
767            // Write X axis data
768            $axisXData = array_keys($series->getValues());
769
770            // c:cat
771            $objWriter->startElement('c:cat');
772            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
773            $objWriter->endElement();
774
775            // Write Y axis data
776            $axisYData = array_values($series->getValues());
777
778            // c:val
779            $objWriter->startElement('c:val');
780            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$' . (1 + count($axisYData)) : '');
781            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
782            $objWriter->endElement();
783
784            $objWriter->endElement();
785
786            ++$seriesIndex;
787        }
788
789        // c:axId
790        $objWriter->startElement('c:axId');
791        $objWriter->writeAttribute('val', '52743552');
792        $objWriter->endElement();
793
794        // c:axId
795        $objWriter->startElement('c:axId');
796        $objWriter->writeAttribute('val', '52749440');
797        $objWriter->endElement();
798
799        $objWriter->endElement();
800    }
801
802    /**
803     * Write Type Bar.
804     *
805     * @param XMLWriter $objWriter XML Writer
806     * @param Chart\Type\Bar $subject
807     */
808    protected function writeTypeBar(XMLWriter $objWriter, Bar $subject, bool $includeSheet = false): void
809    {
810        // c:barChart
811        $objWriter->startElement('c:barChart');
812
813        // c:barDir
814        $objWriter->startElement('c:barDir');
815        $objWriter->writeAttribute('val', $subject->getBarDirection());
816        $objWriter->endElement();
817
818        // c:grouping
819        $objWriter->startElement('c:grouping');
820        $objWriter->writeAttribute('val', $subject->getBarGrouping());
821        $objWriter->endElement();
822
823        // Write series
824        $seriesIndex = 0;
825        foreach ($subject->getSeries() as $series) {
826            // c:ser
827            $objWriter->startElement('c:ser');
828
829            // c:idx
830            $objWriter->startElement('c:idx');
831            $objWriter->writeAttribute('val', $seriesIndex);
832            $objWriter->endElement();
833
834            // c:order
835            $objWriter->startElement('c:order');
836            $objWriter->writeAttribute('val', $seriesIndex);
837            $objWriter->endElement();
838
839            // c:tx
840            $objWriter->startElement('c:tx');
841            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(2 + $seriesIndex) . '$1' : '');
842            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
843            $objWriter->endElement();
844
845            // Fills for points?
846            $dataPointFills = $series->getDataPointFills();
847            foreach ($dataPointFills as $key => $value) {
848                // c:dPt
849                $objWriter->startElement('c:dPt');
850
851                // c:idx
852                $this->writeElementWithValAttribute($objWriter, 'c:idx', (string) $key);
853
854                if (Fill::FILL_NONE != $value->getFillType()) {
855                    // c:spPr
856                    $objWriter->startElement('c:spPr');
857                    // Write fill
858                    $this->writeFill($objWriter, $value);
859                    // ## c:spPr
860                    $objWriter->endElement();
861                }
862
863                // ## c:dPt
864                $objWriter->endElement();
865            }
866
867            // c:dLbls
868            $objWriter->startElement('c:dLbls');
869
870            if ($series->hasDlblNumFormat()) {
871                //c:numFmt
872                $objWriter->startElement('c:numFmt');
873                $objWriter->writeAttribute('formatCode', $series->getDlblNumFormat());
874                $objWriter->writeAttribute('sourceLinked', '0');
875                $objWriter->endElement();
876            }
877
878            // c:txPr
879            $objWriter->startElement('c:txPr');
880
881            // a:bodyPr
882            $objWriter->writeElement('a:bodyPr');
883
884            // a:lstStyle
885            $objWriter->writeElement('a:lstStyle');
886
887            // a:p
888            $objWriter->startElement('a:p');
889
890            // a:pPr
891            $objWriter->startElement('a:pPr');
892
893            // a:defRPr
894            $objWriter->startElement('a:defRPr');
895            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
896            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
897            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
898            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
899            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
900            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
901            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
902
903            // a:solidFill
904            $objWriter->startElement('a:solidFill');
905            $this->writeColor($objWriter, $series->getFont()->getColor());
906            $objWriter->endElement();
907
908            // a:latin
909            $objWriter->startElement('a:latin');
910            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
911            $objWriter->endElement();
912
913            // a:ea
914            $objWriter->startElement('a:ea');
915            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
916            $objWriter->endElement();
917
918            // >a:defRPr
919            $objWriter->endElement();
920            // >a:pPr
921            $objWriter->endElement();
922
923            // a:endParaRPr
924            $objWriter->startElement('a:endParaRPr');
925            $objWriter->writeAttribute('lang', 'en-US');
926            $objWriter->writeAttribute('dirty', '0');
927            $objWriter->endElement();
928
929            // >a:p
930            $objWriter->endElement();
931            // >a:lstStyle
932            $objWriter->endElement();
933
934            // c:dLblPos
935            $this->writeElementWithValAttribute($objWriter, 'c:dLblPos', $series->getLabelPosition());
936
937            // c:showVal
938            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
939
940            // c:showCatName
941            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
942
943            // c:showSerName
944            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
945
946            // c:showPercent
947            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
948
949            // c:separator
950            $objWriter->writeElement('c:separator', $series->hasShowSeparator() ? $series->getSeparator() : '');
951
952            // c:showLeaderLines
953            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
954
955            $objWriter->endElement();
956
957            // c:spPr
958            if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
959                // c:spPr
960                $objWriter->startElement('c:spPr');
961                // Write fill
962                $this->writeFill($objWriter, $series->getFill());
963                // ## c:spPr
964                $objWriter->endElement();
965            }
966
967            // Write X axis data
968            $axisXData = array_keys($series->getValues());
969
970            // c:cat
971            $objWriter->startElement('c:cat');
972            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
973            $objWriter->endElement();
974
975            // Write Y axis data
976            $axisYData = array_values($series->getValues());
977
978            // c:val
979            $objWriter->startElement('c:val');
980            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$' . (1 + count($axisYData)) : '');
981            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
982            $objWriter->endElement();
983
984            $objWriter->endElement();
985
986            ++$seriesIndex;
987        }
988
989        // c:gapWidth
990        $objWriter->startElement('c:gapWidth');
991        $objWriter->writeAttribute('val', $subject->getGapWidthPercent());
992        $objWriter->endElement();
993
994        // c:overlap
995        $objWriter->startElement('c:overlap');
996        $objWriter->writeAttribute('val', $subject->getOverlapWidthPercent());
997        $objWriter->endElement();
998
999        // c:axId
1000        $objWriter->startElement('c:axId');
1001        $objWriter->writeAttribute('val', '52743552');
1002        $objWriter->endElement();
1003
1004        // c:axId
1005        $objWriter->startElement('c:axId');
1006        $objWriter->writeAttribute('val', '52749440');
1007        $objWriter->endElement();
1008
1009        // c:extLst
1010        $objWriter->startElement('c:extLst');
1011        $objWriter->endElement();
1012
1013        $objWriter->endElement();
1014    }
1015
1016    /**
1017     * Write Type Bar3D.
1018     *
1019     * @param XMLWriter $objWriter XML Writer
1020     * @param Chart\Type\Bar3D $subject
1021     */
1022    protected function writeTypeBar3D(XMLWriter $objWriter, Bar3D $subject, bool $includeSheet = false): void
1023    {
1024        // c:bar3DChart
1025        $objWriter->startElement('c:bar3DChart');
1026
1027        // c:barDir
1028        $objWriter->startElement('c:barDir');
1029        $objWriter->writeAttribute('val', $subject->getBarDirection());
1030        $objWriter->endElement();
1031
1032        // c:grouping
1033        $objWriter->startElement('c:grouping');
1034        $objWriter->writeAttribute('val', $subject->getBarGrouping());
1035        $objWriter->endElement();
1036
1037        // Write series
1038        $seriesIndex = 0;
1039        foreach ($subject->getSeries() as $series) {
1040            // c:ser
1041            $objWriter->startElement('c:ser');
1042
1043            // c:idx
1044            $objWriter->startElement('c:idx');
1045            $objWriter->writeAttribute('val', $seriesIndex);
1046            $objWriter->endElement();
1047
1048            // c:order
1049            $objWriter->startElement('c:order');
1050            $objWriter->writeAttribute('val', $seriesIndex);
1051            $objWriter->endElement();
1052
1053            // c:tx
1054            $objWriter->startElement('c:tx');
1055            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(2 + $seriesIndex) . '$1' : '');
1056            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1057            $objWriter->endElement();
1058
1059            // Fills for points?
1060            $dataPointFills = $series->getDataPointFills();
1061            foreach ($dataPointFills as $key => $value) {
1062                // c:dPt
1063                $objWriter->startElement('c:dPt');
1064
1065                // c:idx
1066                $this->writeElementWithValAttribute($objWriter, 'c:idx', (string) $key);
1067
1068                if (Fill::FILL_NONE != $value->getFillType()) {
1069                    // c:spPr
1070                    $objWriter->startElement('c:spPr');
1071                    // Write fill
1072                    $this->writeFill($objWriter, $value);
1073                    // ## c:spPr
1074                    $objWriter->endElement();
1075                }
1076
1077                // ## c:dPt
1078                $objWriter->endElement();
1079            }
1080
1081            // c:dLbls
1082            $objWriter->startElement('c:dLbls');
1083
1084            // c:txPr
1085            $objWriter->startElement('c:txPr');
1086
1087            // a:bodyPr
1088            $objWriter->writeElement('a:bodyPr', null);
1089
1090            // a:lstStyle
1091            $objWriter->writeElement('a:lstStyle', null);
1092
1093            // a:p
1094            $objWriter->startElement('a:p');
1095
1096            // a:pPr
1097            $objWriter->startElement('a:pPr');
1098
1099            // a:defRPr
1100            $objWriter->startElement('a:defRPr');
1101
1102            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1103            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1104            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1105            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1106            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1107            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1108            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1109
1110            // Font - a:solidFill
1111            $objWriter->startElement('a:solidFill');
1112
1113            $this->writeColor($objWriter, $series->getFont()->getColor());
1114
1115            $objWriter->endElement();
1116
1117            // Font - a:latin
1118            $objWriter->startElement('a:latin');
1119            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1120            $objWriter->endElement();
1121            // a:ea
1122            $objWriter->startElement('a:ea');
1123            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1124            $objWriter->endElement();
1125
1126            $objWriter->endElement();
1127
1128            $objWriter->endElement();
1129
1130            // a:endParaRPr
1131            $objWriter->startElement('a:endParaRPr');
1132            $objWriter->writeAttribute('lang', 'en-US');
1133            $objWriter->writeAttribute('dirty', '0');
1134            $objWriter->endElement();
1135
1136            $objWriter->endElement();
1137
1138            $objWriter->endElement();
1139
1140            // c:showVal
1141            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1142
1143            // c:showCatName
1144            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1145
1146            // c:showSerName
1147            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1148
1149            // c:showPercent
1150            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1151
1152            // c:showLeaderLines
1153            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1154
1155            $objWriter->endElement();
1156
1157            // c:spPr
1158            if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
1159                // c:spPr
1160                $objWriter->startElement('c:spPr');
1161                // Write fill
1162                $this->writeFill($objWriter, $series->getFill());
1163                // ## c:spPr
1164                $objWriter->endElement();
1165            }
1166
1167            // Write X axis data
1168            $axisXData = array_keys($series->getValues());
1169
1170            // c:cat
1171            $objWriter->startElement('c:cat');
1172            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1173            $objWriter->endElement();
1174
1175            // Write Y axis data
1176            $axisYData = array_values($series->getValues());
1177
1178            // c:val
1179            $objWriter->startElement('c:val');
1180            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$' . (1 + count($axisYData)) : '');
1181            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1182            $objWriter->endElement();
1183
1184            $objWriter->endElement();
1185
1186            ++$seriesIndex;
1187        }
1188
1189        // c:gapWidth
1190        $objWriter->startElement('c:gapWidth');
1191        $objWriter->writeAttribute('val', $subject->getGapWidthPercent());
1192        $objWriter->endElement();
1193
1194        // c:axId
1195        $objWriter->startElement('c:axId');
1196        $objWriter->writeAttribute('val', '52743552');
1197        $objWriter->endElement();
1198
1199        // c:axId
1200        $objWriter->startElement('c:axId');
1201        $objWriter->writeAttribute('val', '52749440');
1202        $objWriter->endElement();
1203
1204        // c:axId
1205        $objWriter->startElement('c:axId');
1206        $objWriter->writeAttribute('val', '0');
1207        $objWriter->endElement();
1208
1209        $objWriter->endElement();
1210    }
1211
1212    /**
1213     * Write Type Pie.
1214     *
1215     * @param XMLWriter $objWriter XML Writer
1216     */
1217    protected function writeTypeDoughnut(XMLWriter $objWriter, Doughnut $subject, bool $includeSheet = false): void
1218    {
1219        // c:pieChart
1220        $objWriter->startElement('c:doughnutChart');
1221
1222        // c:varyColors
1223        $objWriter->startElement('c:varyColors');
1224        $objWriter->writeAttribute('val', '1');
1225        $objWriter->endElement();
1226
1227        // Write series
1228        $seriesIndex = 0;
1229        foreach ($subject->getSeries() as $series) {
1230            // c:ser
1231            $objWriter->startElement('c:ser');
1232
1233            // c:idx
1234            $objWriter->startElement('c:idx');
1235            $objWriter->writeAttribute('val', $seriesIndex);
1236            $objWriter->endElement();
1237
1238            // c:order
1239            $objWriter->startElement('c:order');
1240            $objWriter->writeAttribute('val', $seriesIndex);
1241            $objWriter->endElement();
1242
1243            // c:tx
1244            $objWriter->startElement('c:tx');
1245            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(2 + $seriesIndex) . '$1' : '');
1246            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1247            $objWriter->endElement();
1248
1249            // Fills for points?
1250            $dataPointFills = $series->getDataPointFills();
1251            foreach ($dataPointFills as $key => $value) {
1252                // c:dPt
1253                $objWriter->startElement('c:dPt');
1254                $this->writeElementWithValAttribute($objWriter, 'c:idx', (string) $key);
1255                // c:dPt/c:spPr
1256                $objWriter->startElement('c:spPr');
1257                $this->writeFill($objWriter, $value);
1258                // c:dPt/##c:spPr
1259                $objWriter->endElement();
1260                // ##c:dPt
1261                $objWriter->endElement();
1262            }
1263
1264            // Write X axis data
1265            $axisXData = array_keys($series->getValues());
1266
1267            // c:cat
1268            $objWriter->startElement('c:cat');
1269            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1270            $objWriter->endElement();
1271
1272            // Write Y axis data
1273            $axisYData = array_values($series->getValues());
1274
1275            // c:val
1276            $objWriter->startElement('c:val');
1277            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$' . (1 + count($axisYData)) : '');
1278            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1279            $objWriter->endElement();
1280
1281            $objWriter->endElement();
1282
1283            ++$seriesIndex;
1284        }
1285
1286        if (isset($series) && is_object($series) && $series instanceof Chart\Series) {
1287            // c:dLbls
1288            $objWriter->startElement('c:dLbls');
1289
1290            if ($series->hasDlblNumFormat()) {
1291                //c:numFmt
1292                $objWriter->startElement('c:numFmt');
1293                $objWriter->writeAttribute('formatCode', $series->getDlblNumFormat());
1294                $objWriter->writeAttribute('sourceLinked', '0');
1295                $objWriter->endElement();
1296            }
1297
1298            // c:dLbls\c:txPr
1299            $objWriter->startElement('c:txPr');
1300            $objWriter->writeElement('a:bodyPr', null);
1301            $objWriter->writeElement('a:lstStyle', null);
1302
1303            // c:dLbls\c:txPr\a:p
1304            $objWriter->startElement('a:p');
1305
1306            // c:dLbls\c:txPr\a:p\a:pPr
1307            $objWriter->startElement('a:pPr');
1308
1309            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr
1310            $objWriter->startElement('a:defRPr');
1311            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1312            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1313            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1314            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1315            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1316            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1317            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1318
1319            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\a:solidFill
1320            $objWriter->startElement('a:solidFill');
1321            $this->writeColor($objWriter, $series->getFont()->getColor());
1322            $objWriter->endElement();
1323
1324            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\a:latin
1325            $objWriter->startElement('a:latin');
1326            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1327            $objWriter->endElement();
1328            // a:ea
1329            $objWriter->startElement('a:ea');
1330            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1331            $objWriter->endElement();
1332
1333            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\
1334            $objWriter->endElement();
1335            // c:dLbls\c:txPr\a:p\a:pPr\
1336            $objWriter->endElement();
1337
1338            // c:dLbls\c:txPr\a:p\a:endParaRPr
1339            $objWriter->startElement('a:endParaRPr');
1340            $objWriter->writeAttribute('lang', 'en-US');
1341            $objWriter->writeAttribute('dirty', '0');
1342            $objWriter->endElement();
1343
1344            // c:dLbls\c:txPr\a:p\
1345            $objWriter->endElement();
1346            // c:dLbls\c:txPr\
1347            $objWriter->endElement();
1348
1349            $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
1350            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1351            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1352            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1353            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1354            $this->writeElementWithValAttribute($objWriter, 'c:showBubbleSize', '0');
1355            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1356
1357            $separator = $series->getSeparator();
1358            if (!empty($separator) && PHP_EOL != $separator) {
1359                // c:dLbls\c:separator
1360                $objWriter->writeElement('c:separator', $separator);
1361            }
1362
1363            // c:dLbls\
1364            $objWriter->endElement();
1365        }
1366
1367        $this->writeElementWithValAttribute($objWriter, 'c:firstSliceAng', '0');
1368        $this->writeElementWithValAttribute($objWriter, 'c:holeSize', (string) $subject->getHoleSize());
1369
1370        $objWriter->endElement();
1371    }
1372
1373    /**
1374     * Write Type Pie.
1375     *
1376     * @param XMLWriter $objWriter XML Writer
1377     */
1378    protected function writeTypePie(XMLWriter $objWriter, Pie $subject, bool $includeSheet = false): void
1379    {
1380        // c:pieChart
1381        $objWriter->startElement('c:pieChart');
1382
1383        // c:varyColors
1384        $objWriter->startElement('c:varyColors');
1385        $objWriter->writeAttribute('val', '1');
1386        $objWriter->endElement();
1387
1388        // Write series
1389        $seriesIndex = 0;
1390        foreach ($subject->getSeries() as $series) {
1391            // c:ser
1392            $objWriter->startElement('c:ser');
1393
1394            // c:idx
1395            $objWriter->startElement('c:idx');
1396            $objWriter->writeAttribute('val', $seriesIndex);
1397            $objWriter->endElement();
1398
1399            // c:order
1400            $objWriter->startElement('c:order');
1401            $objWriter->writeAttribute('val', $seriesIndex);
1402            $objWriter->endElement();
1403
1404            // c:tx
1405            $objWriter->startElement('c:tx');
1406            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(2 + $seriesIndex) . '$1' : '');
1407            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1408            $objWriter->endElement();
1409
1410            // Fills for points?
1411            $dataPointFills = $series->getDataPointFills();
1412            foreach ($dataPointFills as $key => $value) {
1413                // c:dPt
1414                $objWriter->startElement('c:dPt');
1415                $this->writeElementWithValAttribute($objWriter, 'c:idx', (string) $key);
1416                // c:dPt/c:spPr
1417                $objWriter->startElement('c:spPr');
1418                $this->writeFill($objWriter, $value);
1419                // c:dPt/##c:spPr
1420                $objWriter->endElement();
1421                // ##c:dPt
1422                $objWriter->endElement();
1423            }
1424
1425            // c:dLbls
1426            $objWriter->startElement('c:dLbls');
1427
1428            if ($series->hasDlblNumFormat()) {
1429                //c:numFmt
1430                $objWriter->startElement('c:numFmt');
1431                $objWriter->writeAttribute('formatCode', $series->getDlblNumFormat());
1432                $objWriter->writeAttribute('sourceLinked', '0');
1433                $objWriter->endElement();
1434            }
1435
1436            // c:txPr
1437            $objWriter->startElement('c:txPr');
1438
1439            // a:bodyPr
1440            $objWriter->writeElement('a:bodyPr', null);
1441
1442            // a:lstStyle
1443            $objWriter->writeElement('a:lstStyle', null);
1444
1445            // a:p
1446            $objWriter->startElement('a:p');
1447
1448            // a:pPr
1449            $objWriter->startElement('a:pPr');
1450
1451            // a:defRPr
1452            $objWriter->startElement('a:defRPr');
1453
1454            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1455            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1456            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1457            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1458            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1459            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1460            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1461
1462            // Font - a:solidFill
1463            $objWriter->startElement('a:solidFill');
1464
1465            $this->writeColor($objWriter, $series->getFont()->getColor());
1466
1467            $objWriter->endElement();
1468
1469            // Font - a:latin
1470            $objWriter->startElement('a:latin');
1471            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1472            $objWriter->endElement();
1473            // a:ea
1474            $objWriter->startElement('a:ea');
1475            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1476            $objWriter->endElement();
1477
1478            $objWriter->endElement();
1479
1480            $objWriter->endElement();
1481
1482            // a:endParaRPr
1483            $objWriter->startElement('a:endParaRPr');
1484            $objWriter->writeAttribute('lang', 'en-US');
1485            $objWriter->writeAttribute('dirty', '0');
1486            $objWriter->endElement();
1487
1488            $objWriter->endElement();
1489
1490            $objWriter->endElement();
1491
1492            // c:dLblPos
1493            $this->writeElementWithValAttribute($objWriter, 'c:dLblPos', $series->getLabelPosition());
1494
1495            // c:showLegendKey
1496            $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
1497
1498            // c:showVal
1499            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1500
1501            // c:showCatName
1502            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1503
1504            // c:showSerName
1505            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1506
1507            // c:showPercent
1508            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1509
1510            // c:showLeaderLines
1511            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1512
1513            $objWriter->endElement();
1514
1515            // Write X axis data
1516            $axisXData = array_keys($series->getValues());
1517
1518            // c:cat
1519            $objWriter->startElement('c:cat');
1520            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1521            $objWriter->endElement();
1522
1523            // Write Y axis data
1524            $axisYData = array_values($series->getValues());
1525
1526            // c:val
1527            $objWriter->startElement('c:val');
1528            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$' . (1 + count($axisYData)) : '');
1529            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1530            $objWriter->endElement();
1531
1532            $objWriter->endElement();
1533
1534            ++$seriesIndex;
1535        }
1536
1537        $objWriter->endElement();
1538    }
1539
1540    /**
1541     * Write Type Pie3D.
1542     *
1543     * @param XMLWriter $objWriter XML Writer
1544     */
1545    protected function writeTypePie3D(XMLWriter $objWriter, Pie3D $subject, bool $includeSheet = false): void
1546    {
1547        // c:pie3DChart
1548        $objWriter->startElement('c:pie3DChart');
1549
1550        // c:varyColors
1551        $objWriter->startElement('c:varyColors');
1552        $objWriter->writeAttribute('val', '1');
1553        $objWriter->endElement();
1554
1555        // Write series
1556        $seriesIndex = 0;
1557        foreach ($subject->getSeries() as $series) {
1558            // c:ser
1559            $objWriter->startElement('c:ser');
1560
1561            // c:idx
1562            $objWriter->startElement('c:idx');
1563            $objWriter->writeAttribute('val', $seriesIndex);
1564            $objWriter->endElement();
1565
1566            // c:order
1567            $objWriter->startElement('c:order');
1568            $objWriter->writeAttribute('val', $seriesIndex);
1569            $objWriter->endElement();
1570
1571            // c:tx
1572            $objWriter->startElement('c:tx');
1573            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(2 + $seriesIndex) . '$1' : '');
1574            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1575            $objWriter->endElement();
1576
1577            // c:explosion
1578            $objWriter->startElement('c:explosion');
1579            $objWriter->writeAttribute('val', $subject->getExplosion());
1580            $objWriter->endElement();
1581
1582            // Fills for points?
1583            $dataPointFills = $series->getDataPointFills();
1584            foreach ($dataPointFills as $key => $value) {
1585                // c:dPt
1586                $objWriter->startElement('c:dPt');
1587                $this->writeElementWithValAttribute($objWriter, 'c:idx', (string) $key);
1588                // c:dPt/c:spPr
1589                $objWriter->startElement('c:spPr');
1590                $this->writeFill($objWriter, $value);
1591                // c:dPt/##c:spPr
1592                $objWriter->endElement();
1593                // ##c:dPt
1594                $objWriter->endElement();
1595            }
1596
1597            // c:dLbls
1598            $objWriter->startElement('c:dLbls');
1599
1600            // c:txPr
1601            $objWriter->startElement('c:txPr');
1602
1603            // a:bodyPr
1604            $objWriter->writeElement('a:bodyPr', null);
1605
1606            // a:lstStyle
1607            $objWriter->writeElement('a:lstStyle', null);
1608
1609            // a:p
1610            $objWriter->startElement('a:p');
1611
1612            // a:pPr
1613            $objWriter->startElement('a:pPr');
1614
1615            // a:defRPr
1616            $objWriter->startElement('a:defRPr');
1617
1618            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1619            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1620            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1621            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1622            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1623            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1624            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1625
1626            // Font - a:solidFill
1627            $objWriter->startElement('a:solidFill');
1628
1629            $this->writeColor($objWriter, $series->getFont()->getColor());
1630
1631            $objWriter->endElement();
1632
1633            // Font - a:latin
1634            $objWriter->startElement('a:latin');
1635            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1636            $objWriter->endElement();
1637            // a:ea
1638            $objWriter->startElement('a:ea');
1639            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1640            $objWriter->endElement();
1641
1642            $objWriter->endElement();
1643
1644            $objWriter->endElement();
1645
1646            // a:endParaRPr
1647            $objWriter->startElement('a:endParaRPr');
1648            $objWriter->writeAttribute('lang', 'en-US');
1649            $objWriter->writeAttribute('dirty', '0');
1650            $objWriter->endElement();
1651
1652            $objWriter->endElement();
1653
1654            $objWriter->endElement();
1655
1656            // c:dLblPos
1657            $this->writeElementWithValAttribute($objWriter, 'c:dLblPos', $series->getLabelPosition());
1658
1659            // c:showVal
1660            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1661
1662            // c:showCatName
1663            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1664
1665            // c:showSerName
1666            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1667
1668            // c:showPercent
1669            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1670
1671            // c:showLeaderLines
1672            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1673
1674            $objWriter->endElement();
1675
1676            // Write X axis data
1677            $axisXData = array_keys($series->getValues());
1678
1679            // c:cat
1680            $objWriter->startElement('c:cat');
1681            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1682            $objWriter->endElement();
1683
1684            // Write Y axis data
1685            $axisYData = array_values($series->getValues());
1686
1687            // c:val
1688            $objWriter->startElement('c:val');
1689            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$' . (1 + count($axisYData)) : '');
1690            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1691            $objWriter->endElement();
1692
1693            $objWriter->endElement();
1694
1695            ++$seriesIndex;
1696        }
1697
1698        $objWriter->endElement();
1699    }
1700
1701    /**
1702     * Write Type Line.
1703     *
1704     * @param XMLWriter $objWriter XML Writer
1705     */
1706    protected function writeTypeLine(XMLWriter $objWriter, Line $subject, bool $includeSheet = false): void
1707    {
1708        // c:lineChart
1709        $objWriter->startElement('c:lineChart');
1710
1711        // c:grouping
1712        $objWriter->startElement('c:grouping');
1713        $objWriter->writeAttribute('val', 'standard');
1714        $objWriter->endElement();
1715
1716        // Write series
1717        $seriesIndex = 0;
1718        foreach ($subject->getSeries() as $series) {
1719            // c:ser
1720            $objWriter->startElement('c:ser');
1721
1722            // c:idx
1723            $objWriter->startElement('c:idx');
1724            $objWriter->writeAttribute('val', $seriesIndex);
1725            $objWriter->endElement();
1726
1727            // c:order
1728            $objWriter->startElement('c:order');
1729            $objWriter->writeAttribute('val', $seriesIndex);
1730            $objWriter->endElement();
1731
1732            // c:tx
1733            $objWriter->startElement('c:tx');
1734            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(2 + $seriesIndex) . '$1' : '');
1735            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1736            $objWriter->endElement();
1737
1738            // c:spPr
1739            $objWriter->startElement('c:spPr');
1740            // Write fill
1741            $this->writeFill($objWriter, $series->getFill());
1742            // Write outline
1743            $this->writeOutline($objWriter, $series->getOutline());
1744            // ## c:spPr
1745            $objWriter->endElement();
1746
1747            // Marker
1748            $this->writeSeriesMarker($objWriter, $series->getMarker());
1749
1750            // c:dLbls
1751            $objWriter->startElement('c:dLbls');
1752
1753            // c:txPr
1754            $objWriter->startElement('c:txPr');
1755
1756            // a:bodyPr
1757            $objWriter->writeElement('a:bodyPr', null);
1758
1759            // a:lstStyle
1760            $objWriter->writeElement('a:lstStyle', null);
1761
1762            // a:p
1763            $objWriter->startElement('a:p');
1764
1765            // a:pPr
1766            $objWriter->startElement('a:pPr');
1767
1768            // a:defRPr
1769            $objWriter->startElement('a:defRPr');
1770
1771            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1772            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1773            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1774            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1775            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1776            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1777            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1778
1779            // Font - a:solidFill
1780            $objWriter->startElement('a:solidFill');
1781
1782            $this->writeColor($objWriter, $series->getFont()->getColor());
1783
1784            $objWriter->endElement();
1785
1786            // Font - a:latin
1787            $objWriter->startElement('a:latin');
1788            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1789            $objWriter->endElement();
1790            // a:ea
1791            $objWriter->startElement('a:ea');
1792            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1793            $objWriter->endElement();
1794
1795            $objWriter->endElement();
1796
1797            $objWriter->endElement();
1798
1799            // a:endParaRPr
1800            $objWriter->startElement('a:endParaRPr');
1801            $objWriter->writeAttribute('lang', 'en-US');
1802            $objWriter->writeAttribute('dirty', '0');
1803            $objWriter->endElement();
1804
1805            $objWriter->endElement();
1806
1807            $objWriter->endElement();
1808
1809            // c:showVal
1810            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1811
1812            // c:showCatName
1813            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1814
1815            // c:showSerName
1816            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1817
1818            // c:showPercent
1819            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1820
1821            // c:showLeaderLines
1822            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1823
1824            // > c:dLbls
1825            $objWriter->endElement();
1826
1827            // Write X axis data
1828            $axisXData = array_keys($series->getValues());
1829
1830            // c:cat
1831            $objWriter->startElement('c:cat');
1832            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1833            $objWriter->endElement();
1834
1835            // Write Y axis data
1836            $axisYData = array_values($series->getValues());
1837
1838            // c:val
1839            $objWriter->startElement('c:val');
1840            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$' . (1 + count($axisYData)) : '');
1841            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1842            $objWriter->endElement();
1843
1844            // c:smooth
1845            $objWriter->startElement('c:smooth');
1846            $objWriter->writeAttribute('val', $subject->isSmooth() ? '1' : '0');
1847            $objWriter->endElement();
1848
1849            $objWriter->endElement();
1850
1851            ++$seriesIndex;
1852        }
1853
1854        // c:marker
1855        $objWriter->startElement('c:marker');
1856        $objWriter->writeAttribute('val', '1');
1857        $objWriter->endElement();
1858
1859        // c:axId
1860        $objWriter->startElement('c:axId');
1861        $objWriter->writeAttribute('val', '52743552');
1862        $objWriter->endElement();
1863
1864        // c:axId
1865        $objWriter->startElement('c:axId');
1866        $objWriter->writeAttribute('val', '52749440');
1867        $objWriter->endElement();
1868
1869        $objWriter->endElement();
1870    }
1871
1872    /**
1873     * Write Type Radar.
1874     *
1875     * @param XMLWriter $objWriter XML Writer
1876     */
1877    protected function writeTypeRadar(XMLWriter $objWriter, Radar $subject, bool $includeSheet = false): void
1878    {
1879        // c:scatterChart
1880        $objWriter->startElement('c:radarChart');
1881
1882        // c:radarStyle
1883        $objWriter->startElement('c:radarStyle');
1884        $objWriter->writeAttribute('val', 'marker');
1885        $objWriter->endElement();
1886
1887        // c:varyColors
1888        $objWriter->startElement('c:varyColors');
1889        $objWriter->writeAttribute('val', '0');
1890        $objWriter->endElement();
1891
1892        // Write series
1893        $seriesIndex = 0;
1894        foreach ($subject->getSeries() as $series) {
1895            // c:ser
1896            $objWriter->startElement('c:ser');
1897
1898            // c:idx
1899            $objWriter->startElement('c:idx');
1900            $objWriter->writeAttribute('val', $seriesIndex);
1901            $objWriter->endElement();
1902
1903            // c:order
1904            $objWriter->startElement('c:order');
1905            $objWriter->writeAttribute('val', $seriesIndex);
1906            $objWriter->endElement();
1907
1908            // c:tx
1909            $objWriter->startElement('c:tx');
1910            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(2 + $seriesIndex) . '$1' : '');
1911            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1912            $objWriter->endElement();
1913
1914            // c:spPr
1915            $objWriter->startElement('c:spPr');
1916            // Write fill
1917            $this->writeFill($objWriter, $series->getFill());
1918            // Write outline
1919            $this->writeOutline($objWriter, $series->getOutline());
1920            // ## c:spPr
1921            $objWriter->endElement();
1922
1923            // Marker
1924            $this->writeSeriesMarker($objWriter, $series->getMarker());
1925
1926            // c:dLbls
1927            $objWriter->startElement('c:dLbls');
1928
1929            // c:txPr
1930            $objWriter->startElement('c:txPr');
1931
1932            // a:bodyPr
1933            $objWriter->writeElement('a:bodyPr', null);
1934
1935            // a:lstStyle
1936            $objWriter->writeElement('a:lstStyle', null);
1937
1938            // a:p
1939            $objWriter->startElement('a:p');
1940
1941            // a:pPr
1942            $objWriter->startElement('a:pPr');
1943
1944            // a:defRPr
1945            $objWriter->startElement('a:defRPr');
1946
1947            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1948            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1949            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1950            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1951            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1952            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '30000');
1953            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-25000');
1954
1955            // Font - a:solidFill
1956            $objWriter->startElement('a:solidFill');
1957
1958            $this->writeColor($objWriter, $series->getFont()->getColor());
1959
1960            $objWriter->endElement();
1961
1962            // Font - a:latin
1963            $objWriter->startElement('a:latin');
1964            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1965            $objWriter->endElement();
1966            // a:ea
1967            $objWriter->startElement('a:ea');
1968            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1969            $objWriter->endElement();
1970
1971            $objWriter->endElement();
1972
1973            $objWriter->endElement();
1974
1975            // a:endParaRPr
1976            $objWriter->startElement('a:endParaRPr');
1977            $objWriter->writeAttribute('lang', 'en-US');
1978            $objWriter->writeAttribute('dirty', '0');
1979            $objWriter->endElement();
1980
1981            $objWriter->endElement();
1982
1983            $objWriter->endElement();
1984
1985            // c:showLegendKey
1986            $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
1987
1988            // c:showVal
1989            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1990
1991            // c:showCatName
1992            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1993
1994            // c:showSerName
1995            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1996
1997            // c:showPercent
1998            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1999
2000            // c:showLeaderLines
2001            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
2002
2003            $objWriter->endElement();
2004
2005            // Write X axis data
2006            $axisXData = array_keys($series->getValues());
2007
2008            // c:cat
2009            $objWriter->startElement('c:cat');
2010            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
2011            $objWriter->endElement();
2012
2013            // Write Y axis data
2014            $axisYData = array_values($series->getValues());
2015
2016            // c:val
2017            $objWriter->startElement('c:val');
2018            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$' . (1 + count($axisYData)) : '');
2019            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
2020            $objWriter->endElement();
2021
2022            $objWriter->endElement();
2023
2024            ++$seriesIndex;
2025        }
2026
2027        // c:axId
2028        $objWriter->startElement('c:axId');
2029        $objWriter->writeAttribute('val', '52743552');
2030        $objWriter->endElement();
2031
2032        // c:axId
2033        $objWriter->startElement('c:axId');
2034        $objWriter->writeAttribute('val', '52749440');
2035        $objWriter->endElement();
2036
2037        $objWriter->endElement();
2038    }
2039
2040    /**
2041     * Write Type Scatter.
2042     */
2043    protected function writeTypeScatter(XMLWriter $objWriter, Scatter $subject, bool $includeSheet = false): void
2044    {
2045        // c:scatterChart
2046        $objWriter->startElement('c:scatterChart');
2047
2048        // c:scatterStyle
2049        $objWriter->startElement('c:scatterStyle');
2050        $objWriter->writeAttribute('val', 'lineMarker');
2051        $objWriter->endElement();
2052
2053        // c:varyColors
2054        $objWriter->startElement('c:varyColors');
2055        $objWriter->writeAttribute('val', '0');
2056        $objWriter->endElement();
2057
2058        // Write series
2059        $seriesIndex = 0;
2060        foreach ($subject->getSeries() as $series) {
2061            // c:ser
2062            $objWriter->startElement('c:ser');
2063
2064            // c:idx
2065            $objWriter->startElement('c:idx');
2066            $objWriter->writeAttribute('val', $seriesIndex);
2067            $objWriter->endElement();
2068
2069            // c:order
2070            $objWriter->startElement('c:order');
2071            $objWriter->writeAttribute('val', $seriesIndex);
2072            $objWriter->endElement();
2073
2074            // c:tx
2075            $objWriter->startElement('c:tx');
2076            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(2 + $seriesIndex) . '$1' : '');
2077            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
2078            $objWriter->endElement();
2079
2080            // c:spPr
2081            $objWriter->startElement('c:spPr');
2082            // Write fill
2083            $this->writeFill($objWriter, $series->getFill());
2084            // Write outline
2085            $this->writeOutline($objWriter, $series->getOutline());
2086            // ## c:spPr
2087            $objWriter->endElement();
2088
2089            // Marker
2090            $this->writeSeriesMarker($objWriter, $series->getMarker());
2091
2092            // c:dLbls
2093            $objWriter->startElement('c:dLbls');
2094
2095            // c:txPr
2096            $objWriter->startElement('c:txPr');
2097
2098            // a:bodyPr
2099            $objWriter->writeElement('a:bodyPr', null);
2100
2101            // a:lstStyle
2102            $objWriter->writeElement('a:lstStyle', null);
2103
2104            // a:p
2105            $objWriter->startElement('a:p');
2106
2107            // a:pPr
2108            $objWriter->startElement('a:pPr');
2109
2110            // a:defRPr
2111            $objWriter->startElement('a:defRPr');
2112
2113            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
2114            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
2115            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
2116            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
2117            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
2118            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
2119            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
2120
2121            // Font - a:solidFill
2122            $objWriter->startElement('a:solidFill');
2123
2124            $this->writeColor($objWriter, $series->getFont()->getColor());
2125
2126            $objWriter->endElement();
2127
2128            // Font - a:latin
2129            $objWriter->startElement('a:latin');
2130            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
2131            $objWriter->endElement();
2132            // a:ea
2133            $objWriter->startElement('a:ea');
2134            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
2135            $objWriter->endElement();
2136
2137            $objWriter->endElement();
2138
2139            $objWriter->endElement();
2140
2141            // a:endParaRPr
2142            $objWriter->startElement('a:endParaRPr');
2143            $objWriter->writeAttribute('lang', 'en-US');
2144            $objWriter->writeAttribute('dirty', '0');
2145            $objWriter->endElement();
2146
2147            $objWriter->endElement();
2148
2149            $objWriter->endElement();
2150
2151            // c:showLegendKey
2152            $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
2153
2154            // c:showVal
2155            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
2156
2157            // c:showCatName
2158            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
2159
2160            // c:showSerName
2161            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
2162
2163            // c:showPercent
2164            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
2165
2166            // c:separator
2167            $separator = $series->getSeparator();
2168            if (!empty($separator) && PHP_EOL != $separator) {
2169                // c:dLbls\c:separator
2170                $objWriter->writeElement('c:separator', $separator);
2171            }
2172
2173            // c:showLeaderLines
2174            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
2175
2176            $objWriter->endElement();
2177
2178            // Write X axis data
2179            $axisXData = array_keys($series->getValues());
2180
2181            // c:xVal
2182            $objWriter->startElement('c:xVal');
2183            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
2184            $objWriter->endElement();
2185
2186            // Write Y axis data
2187            $axisYData = array_values($series->getValues());
2188
2189            // c:yVal
2190            $objWriter->startElement('c:yVal');
2191            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 2) . '$' . (1 + count($axisYData)) : '');
2192            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
2193            $objWriter->endElement();
2194
2195            // c:smooth
2196            $objWriter->startElement('c:smooth');
2197            $objWriter->writeAttribute('val', $subject->isSmooth() ? '1' : '0');
2198            $objWriter->endElement();
2199
2200            $objWriter->endElement();
2201
2202            ++$seriesIndex;
2203        }
2204
2205        // c:axId
2206        $objWriter->startElement('c:axId');
2207        $objWriter->writeAttribute('val', '52743552');
2208        $objWriter->endElement();
2209
2210        // c:axId
2211        $objWriter->startElement('c:axId');
2212        $objWriter->writeAttribute('val', '52749440');
2213        $objWriter->endElement();
2214
2215        $objWriter->endElement();
2216    }
2217
2218    /**
2219     * Write chart relationships to XML format.
2220     *
2221     * @return string XML Output
2222     */
2223    protected function writeChartRelationships(Chart $pChart): string
2224    {
2225        // Create XML writer
2226        $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
2227
2228        // XML header
2229        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
2230
2231        // Relationships
2232        $objWriter->startElement('Relationships');
2233        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
2234
2235        // Write spreadsheet relationship?
2236        if ($pChart->hasIncludedSpreadsheet()) {
2237            $this->writeRelationship($objWriter, 1, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package', '../embeddings/' . $pChart->getIndexedFilename() . '.xlsx');
2238        }
2239
2240        $objWriter->endElement();
2241
2242        // Return
2243        return $objWriter->getData();
2244    }
2245
2246    protected function writeSeriesMarker(XMLWriter $objWriter, Chart\Marker $marker): void
2247    {
2248        // c:marker
2249        $objWriter->startElement('c:marker');
2250        // c:marker > c:symbol
2251        $objWriter->startElement('c:symbol');
2252        $objWriter->writeAttribute('val', $marker->getSymbol());
2253        $objWriter->endElement();
2254
2255        // Size if different of none
2256        if (Chart\Marker::SYMBOL_NONE != $marker->getSymbol()) {
2257            $markerSize = (int) $marker->getSize();
2258            if ($markerSize < 2) {
2259                $markerSize = 2;
2260            }
2261            if ($markerSize > 72) {
2262                $markerSize = 72;
2263            }
2264
2265            /*
2266             * c:marker > c:size
2267             * Size in points
2268             * @link : https://msdn.microsoft.com/en-us/library/hh658135(v=office.12).aspx
2269             */
2270            $objWriter->startElement('c:size');
2271            $objWriter->writeAttribute('val', $markerSize);
2272            $objWriter->endElement();
2273        }
2274
2275        // // c:marker > c:spPr
2276        $objWriter->startElement('c:spPr');
2277        $this->writeFill($objWriter, $marker->getFill());
2278        $this->writeBorder($objWriter, $marker->getBorder(), '', true);
2279        $objWriter->endElement();
2280
2281        // > c:marker
2282        $objWriter->endElement();
2283    }
2284
2285    protected function writeAxis(XMLWriter $objWriter, Chart\Axis $oAxis, string $typeAxis, Chart\Type\AbstractType $typeChart): void
2286    {
2287        if (Chart\Axis::AXIS_X != $typeAxis && Chart\Axis::AXIS_Y != $typeAxis) {
2288            return;
2289        }
2290
2291        $crossesAt = $oAxis->getCrossesAt();
2292        $orientation = $oAxis->isReversedOrder() ? 'maxMin' : 'minMax';
2293
2294        if (Chart\Axis::AXIS_X == $typeAxis) {
2295            $mainElement = 'c:catAx';
2296            $axIdVal = '52743552';
2297            $axPosVal = $crossesAt === 'max' ? 't' : 'b';
2298            $crossAxVal = '52749440';
2299        } else {
2300            $mainElement = 'c:valAx';
2301            $axIdVal = '52749440';
2302            $axPosVal = $crossesAt === 'max' ? 'r' : 'l';
2303            $crossAxVal = '52743552';
2304        }
2305
2306        // $mainElement
2307        $objWriter->startElement($mainElement);
2308
2309        // $mainElement > c:axId
2310        $objWriter->startElement('c:axId');
2311        $objWriter->writeAttribute('val', $axIdVal);
2312        $objWriter->endElement();
2313
2314        // $mainElement > c:scaling
2315        $objWriter->startElement('c:scaling');
2316
2317        // $mainElement > c:scaling > c:orientation
2318        $objWriter->startElement('c:orientation');
2319        $objWriter->writeAttribute('val', $orientation);
2320        $objWriter->endElement();
2321
2322        if (null !== $oAxis->getMaxBounds()) {
2323            $objWriter->startElement('c:max');
2324            $objWriter->writeAttribute('val', $oAxis->getMaxBounds());
2325            $objWriter->endElement();
2326        }
2327
2328        if (null !== $oAxis->getMinBounds()) {
2329            $objWriter->startElement('c:min');
2330            $objWriter->writeAttribute('val', $oAxis->getMinBounds());
2331            $objWriter->endElement();
2332        }
2333
2334        // $mainElement > ##c:scaling
2335        $objWriter->endElement();
2336
2337        // $mainElement > c:delete
2338        $objWriter->startElement('c:delete');
2339        $objWriter->writeAttribute('val', $oAxis->isVisible() ? '0' : '1');
2340        $objWriter->endElement();
2341
2342        // $mainElement > c:axPos
2343        $objWriter->startElement('c:axPos');
2344        $objWriter->writeAttribute('val', $axPosVal);
2345        $objWriter->endElement();
2346
2347        $oMajorGridlines = $oAxis->getMajorGridlines();
2348        if ($oMajorGridlines instanceof Gridlines) {
2349            $objWriter->startElement('c:majorGridlines');
2350
2351            $this->writeAxisGridlines($objWriter, $oMajorGridlines);
2352
2353            $objWriter->endElement();
2354        }
2355
2356        $oMinorGridlines = $oAxis->getMinorGridlines();
2357        if ($oMinorGridlines instanceof Gridlines) {
2358            $objWriter->startElement('c:minorGridlines');
2359
2360            $this->writeAxisGridlines($objWriter, $oMinorGridlines);
2361
2362            $objWriter->endElement();
2363        }
2364
2365        if ('' != $oAxis->getTitle()) {
2366            // c:title
2367            $objWriter->startElement('c:title');
2368
2369            // c:tx
2370            $objWriter->startElement('c:tx');
2371
2372            // c:rich
2373            $objWriter->startElement('c:rich');
2374
2375            // a:bodyPr
2376            $objWriter->startElement('a:bodyPr');
2377            $objWriter->writeAttributeIf($oAxis->getTitleRotation() != 0, 'rot', CommonDrawing::degreesToAngle((int) $oAxis->getTitleRotation()));
2378            $objWriter->endElement();
2379
2380            // a:lstStyle
2381            $objWriter->writeElement('a:lstStyle', null);
2382
2383            // a:p
2384            $objWriter->startElement('a:p');
2385
2386            // a:pPr
2387            $objWriter->startElement('a:pPr');
2388
2389            // a:defRPr
2390            $objWriter->startElement('a:defRPr');
2391
2392            $objWriter->writeAttribute('b', ($oAxis->getFont()->isBold() ? 'true' : 'false'));
2393            $objWriter->writeAttribute('i', ($oAxis->getFont()->isItalic() ? 'true' : 'false'));
2394            $objWriter->writeAttribute('strike', ($oAxis->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
2395            $objWriter->writeAttribute('sz', ($oAxis->getFont()->getSize() * 100));
2396            $objWriter->writeAttribute('u', $oAxis->getFont()->getUnderline());
2397            $objWriter->writeAttributeIf($oAxis->getFont()->isSuperScript(), 'baseline', '300000');
2398            $objWriter->writeAttributeIf($oAxis->getFont()->isSubScript(), 'baseline', '-250000');
2399
2400            // Font - a:solidFill
2401            $objWriter->startElement('a:solidFill');
2402            $this->writeColor($objWriter, $oAxis->getFont()->getColor());
2403            $objWriter->endElement();
2404
2405            // Font - a:latin
2406            $objWriter->startElement('a:latin');
2407            $objWriter->writeAttribute('typeface', $oAxis->getFont()->getName());
2408            $objWriter->endElement();
2409            // a:ea
2410            $objWriter->startElement('a:ea');
2411            $objWriter->writeAttribute('typeface', $oAxis->getFont()->getName());
2412            $objWriter->endElement();
2413
2414            $objWriter->endElement();
2415
2416            // ## a:pPr
2417            $objWriter->endElement();
2418
2419            // a:r
2420            $objWriter->startElement('a:r');
2421
2422            // a:rPr
2423            $objWriter->startElement('a:rPr');
2424            $objWriter->writeAttribute('lang', 'en-US');
2425            $objWriter->writeAttribute('dirty', '0');
2426            $objWriter->endElement();
2427
2428            // a:t
2429            $objWriter->writeElement('a:t', $oAxis->getTitle());
2430
2431            // ## a:r
2432            $objWriter->endElement();
2433
2434            // a:endParaRPr
2435            $objWriter->startElement('a:endParaRPr');
2436            $objWriter->writeAttribute('lang', 'en-US');
2437            $objWriter->writeAttribute('dirty', '0');
2438            $objWriter->endElement();
2439
2440            // ## a:p
2441            $objWriter->endElement();
2442
2443            // ## c:rich
2444            $objWriter->endElement();
2445
2446            // ## c:tx
2447            $objWriter->endElement();
2448
2449            // ## c:title
2450            $objWriter->endElement();
2451        }
2452
2453        // c:numFmt
2454        $objWriter->startElement('c:numFmt');
2455        $objWriter->writeAttribute('formatCode', $oAxis->getFormatCode());
2456        $objWriter->writeAttribute('sourceLinked', '1');
2457        $objWriter->endElement();
2458
2459        // c:majorTickMark
2460        $objWriter->startElement('c:majorTickMark');
2461        $objWriter->writeAttribute('val', $oAxis->getMajorTickMark());
2462        $objWriter->endElement();
2463
2464        // c:minorTickMark
2465        $objWriter->startElement('c:minorTickMark');
2466        $objWriter->writeAttribute('val', $oAxis->getMinorTickMark());
2467        $objWriter->endElement();
2468
2469        // c:tickLblPos
2470        $objWriter->startElement('c:tickLblPos');
2471        $objWriter->writeAttribute('val', $oAxis->getTickLabelPosition());
2472        $objWriter->endElement();
2473
2474        // c:spPr
2475        $objWriter->startElement('c:spPr');
2476        $this->writeOutline($objWriter, $oAxis->getOutline());
2477        $objWriter->endElement();
2478
2479        // c:txPr
2480        $objWriter->startElement('c:txPr');
2481
2482        // a:bodyPr
2483        $objWriter->writeElement('a:bodyPr', null);
2484
2485        // a:lstStyle
2486        $objWriter->writeElement('a:lstStyle', null);
2487
2488        // a:p
2489        $objWriter->startElement('a:p');
2490
2491        // a:pPr
2492        $objWriter->startElement('a:pPr');
2493
2494        // a:defRPr
2495        $objWriter->startElement('a:defRPr');
2496        $objWriter->writeAttribute('b', ($oAxis->getTickLabelFont()->isBold() ? 'true' : 'false'));
2497        $objWriter->writeAttribute('i', ($oAxis->getTickLabelFont()->isItalic() ? 'true' : 'false'));
2498        $objWriter->writeAttribute('strike', ($oAxis->getTickLabelFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
2499        $objWriter->writeAttribute('sz', ($oAxis->getTickLabelFont()->getSize() * 100));
2500        $objWriter->writeAttribute('u', $oAxis->getTickLabelFont()->getUnderline());
2501        $objWriter->writeAttributeIf($oAxis->getTickLabelFont()->isSuperScript(), 'baseline', '300000');
2502        $objWriter->writeAttributeIf($oAxis->getTickLabelFont()->isSubScript(), 'baseline', '-250000');
2503
2504        // Font - a:solidFill
2505        $objWriter->startElement('a:solidFill');
2506        $this->writeColor($objWriter, $oAxis->getTickLabelFont()->getColor());
2507        $objWriter->endElement();
2508
2509        // Font - a:latin
2510        $objWriter->startElement('a:latin');
2511        $objWriter->writeAttribute('typeface', $oAxis->getTickLabelFont()->getName());
2512        $objWriter->endElement();
2513
2514        // Font - a:ea
2515        $objWriter->startElement('a:ea');
2516        $objWriter->writeAttribute('typeface', $oAxis->getTickLabelFont()->getName());
2517        $objWriter->endElement();
2518
2519        //## a:defRPr
2520        $objWriter->endElement();
2521
2522        //## a:pPr
2523        $objWriter->endElement();
2524
2525        // a:endParaRPr
2526        $objWriter->startElement('a:endParaRPr');
2527        $objWriter->writeAttribute('lang', 'en-US');
2528        $objWriter->writeAttribute('dirty', '0');
2529        $objWriter->endElement();
2530
2531        // ## a:p
2532        $objWriter->endElement();
2533
2534        // ## c:txPr
2535        $objWriter->endElement();
2536
2537        // c:crossAx
2538        $objWriter->startElement('c:crossAx');
2539        $objWriter->writeAttribute('val', $crossAxVal);
2540        $objWriter->endElement();
2541
2542        // c:crosses "autoZero" | "min" | "max" | custom string value
2543        if (in_array($crossesAt, ['autoZero', 'min', 'max'])) {
2544            $objWriter->startElement('c:crosses');
2545            $objWriter->writeAttribute('val', $crossesAt);
2546            $objWriter->endElement();
2547        } else {
2548            $objWriter->startElement('c:crossesAt');
2549            $objWriter->writeAttribute('val', $crossesAt);
2550            $objWriter->endElement();
2551        }
2552
2553        if (Chart\Axis::AXIS_X == $typeAxis) {
2554            // c:lblAlgn
2555            $objWriter->startElement('c:lblAlgn');
2556            $objWriter->writeAttribute('val', 'ctr');
2557            $objWriter->endElement();
2558
2559            // c:lblOffset
2560            $objWriter->startElement('c:lblOffset');
2561            $objWriter->writeAttribute('val', '100');
2562            $objWriter->endElement();
2563
2564            // c:majorUnit
2565            if ($oAxis->getMajorUnit() !== null) {
2566                $objWriter->startElement('c:tickLblSkip');
2567                $objWriter->writeAttribute('val', $oAxis->getMajorUnit());
2568                $objWriter->endElement();
2569            }
2570        }
2571
2572        if (Chart\Axis::AXIS_Y == $typeAxis) {
2573            // c:crossBetween
2574            $objWriter->startElement('c:crossBetween');
2575            // midCat : Position Axis On Tick Marks
2576            // between : Between Tick Marks
2577            if ($typeChart instanceof Area) {
2578                $objWriter->writeAttribute('val', 'midCat');
2579            } else {
2580                $objWriter->writeAttribute('val', 'between');
2581            }
2582            $objWriter->endElement();
2583
2584            // c:majorUnit
2585            if ($oAxis->getMajorUnit() !== null) {
2586                $objWriter->startElement('c:majorUnit');
2587                $objWriter->writeAttribute('val', $oAxis->getMajorUnit());
2588                $objWriter->endElement();
2589            }
2590
2591            // c:minorUnit
2592            if ($oAxis->getMinorUnit() !== null) {
2593                $objWriter->startElement('c:minorUnit');
2594                $objWriter->writeAttribute('val', $oAxis->getMinorUnit());
2595                $objWriter->endElement();
2596            }
2597        }
2598
2599        $objWriter->endElement();
2600    }
2601
2602    protected function writeAxisGridlines(XMLWriter $objWriter, Gridlines $oGridlines): void
2603    {
2604        // c:spPr
2605        $objWriter->startElement('c:spPr');
2606
2607        // Outline
2608        $this->writeOutline($objWriter, $oGridlines->getOutline());
2609
2610        // ##c:spPr
2611        $objWriter->endElement();
2612    }
2613}